#include "dco.h"

/** This infrastructure assumes that SMCLK is at 1MHz and ACLK is at 32KHz.
 * MCLK however, can be configured to be at varying rates.
 */
void
configureDCO (int dco_mhz)
{
  int divs;
  
  /* ACLK is to be set to REFOCLK, which is 32KHz (2^15Hz)
   *
   * DCO is to be set to dco_mhz * 2^20 Hz.  The clock divider is the
   * minimum value of 2.
   *
   * MCLK is set to DCOCLKDIV, or ~ 2 MHz
   *
   * SMLCK is set to DCOCLKDIV / 2, or ~ 1MHz
   *
   * The technique used here is cribbed from the TI Example programs
   * for the CC430, cc430x613x_UCS_2.c.  */

  /* Use REFOCLK as the FLL input.  Use FLLREFDIV value 1 (selected
   * by bits 000) */
  UCSCTL3 = SELREF__REFOCLK;

  /* The appropriate value for DCORSEL is obtained from the DCO
   * Frequency table of the device datasheet.  Find the DCORSEL
   * value from that table where the minimum frequency with DCOx=31
   * is closest to your desired DCO frequency. */
  __bis_SR_register(SCG0);               // Disable the FLL control loop
  UCSCTL0 = 0x0000;                         // Set lowest possible DCOx, MODx

  switch (dco_mhz) {
    case DCO_2MHz_RSEL2:
    default:
      UCSCTL1 = DCORSEL_2;
      UCSCTL2 = FLLD_1 + 31;
      divs = DIVS__1;
      break;
    case DCO_4MHz_RSEL3:
      UCSCTL1 = DCORSEL_3;
      UCSCTL2 = FLLD_1 + 63;
      divs = DIVS__2;
      break;
    case DCO_8MHz_RSEL3:
      UCSCTL1 = DCORSEL_3;
      UCSCTL2 = FLLD_1 + 127;
      divs = DIVS__4;
      break;
    case DCO_8MHz_RSEL4:
      UCSCTL1 = DCORSEL_4;
      UCSCTL2 = FLLD_1 + 127;
      divs = DIVS__4;
      break;
    case DCO_16MHz_RSEL4:
      UCSCTL1 = DCORSEL_4;
      UCSCTL2 = FLLD_1 + 255;
      divs = DIVS__8;
      break;
    case DCO_16MHz_RSEL5:
      UCSCTL1 = DCORSEL_5;
      UCSCTL2 = FLLD_1 + 255;
      divs = DIVS__8;
      break;
    case DCO_32MHz_RSEL5:
      UCSCTL1 = DCORSEL_5;
      UCSCTL2 = FLLD_1 + 511;
      divs = DIVS__16;
      break;
    case DCO_32MHz_RSEL6:
      UCSCTL1 = DCORSEL_6;
      UCSCTL2 = FLLD_1 + 511;
      divs = DIVS__16;
      break;
  }

  __bic_SR_register(SCG0);               // Enable the FLL control loop

#if 0 /* No __delay_cycles intrinsic on MSPGCC */
  // Worst-case settling time for the DCO when the DCO range bits have been
  // changed is n x 32 x 32 x f_MCLK / f_FLL_reference. See UCS chapter in 5xx
  // UG for optimization.
  // 32 x 32 x 8 MHz / 32,768 Hz = 250000 = MCLK cycles for DCO to settle
  __delay_cycles(250000);
#endif

  // Loop until DCO fault flag is cleared.  Ignore OFIFG, since it
  // incorporates XT1 and XT2 fault detection.
  do {
    UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + XT1HFOFFG + DCOFFG);
    // Clear XT2,XT1,DCO fault flags
    SFRIFG1 &= ~OFIFG;                      // Clear fault flags
  } while (UCSCTL7 & DCOFFG); // Test DCO fault flag

  /* Use REFOCLK for ACLK, and DCOCLKDIV for MCLK and SMCLK */
  UCSCTL4 = SELA__REFOCLK | SELS__DCOCLKDIV | SELM__DCOCLKDIV;

  /* DIVPA routes ACLK to external pin, undivided
   * DIVA uses ACLK at 2^15 Hz, undivided
   * DIVS (SMCLK) uses DCOCLKDIV / 2 to produce 2^20Hz
   * DIVM (MCLK) uses DCOCLKDIV to produce 2^21Hz, undivided
   */
  UCSCTL5 = DIVPA__1 | DIVA__1 | divs | DIVM__1;
}


/* f_dco = flld * (flln + 1) * (f_refclk / fllrefdiv)
 *
 * @param flld A value n such that 2^n produces the desired divider;
 * e.g., use 1 to divide by 2.  PUC value is 1.
 *
 * @param flln Positive number of multiplier bits, generally (2^x)-1
 * for some value of x.  PUC value is 31.
 *
 * @param fllrefdiv A value n.  For n <= 3, the reference divisor is
 * 2^n.  For n==4 the reference divisor is 12, and for n>=5 the
 * divisor is 16.
 *
 */
